<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.dom.TextRange</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.dom');
  goog.require('goog.dom.Range');
  goog.require('goog.dom.ControlRange');
  goog.require('goog.dom.TextRange');
  goog.require('goog.math.Coordinate');
  goog.require('goog.style');
  goog.require('goog.testing.ExpectedFailures');
  goog.require('goog.testing.dom');
  goog.require('goog.testing.jsunit');
  goog.require('goog.userAgent');
</script>
</head>
<body>
  <div id="test1"></div>
  <div id="test2">
    <img id="logo" src="http://www.google.com/intl/en_ALL/images/logo.gif">
  </div>
  <!-- Omit whitespace here to ensure no extra text nodes are included. -->
  <table id="table"><tbody id="tbody"><tr id="tr1"><td id="td11">a</td
      ><td id="td12">b</td></tr><tr id="tr2"><td id="td21">c</td><td id="td22"
      >d</td></tr></tbody></table>
  <table id="table2">
    <tr>
      <td>moof</td>
    </tr>
    <tr>
      <td id="table2td">
        <div id="table2div">foo<img id="logo2"
               src="http://www.google.com/intl/en_ALL/images/logo.gif">bar</div>
      </td>
    </tr>
  </table>
  <!-- Omit whitespace here to ensure no extra text nodes are included. -->
  <div id="test3Rtl" dir="rtl"><span>position</span><img id="logo3Rtl"
      src="http://www.google.com/intl/en_ALL/images/logo.gif"><span
      >test</span></div>
  <!-- Omit whitespace here to ensure no extra text nodes are included. -->
  <div id="test3"><span>position</span><img id="logo3"
      src="http://www.google.com/intl/en_ALL/images/logo.gif"><span
      >test</span></div>

<script>
  var logo = goog.dom.getElement('logo');
  var logo2 = goog.dom.getElement('logo2');
  var logo3 = goog.dom.getElement('logo3');
  var logo3Rtl = goog.dom.getElement('logo3Rtl');
  var table = goog.dom.getElement('table');
  var table2 = goog.dom.getElement('table2');
  var table2div = goog.dom.getElement('table2div');
  var test3 = goog.dom.getElement('test3');
  var test3Rtl = goog.dom.getElement('test3Rtl');

  var expectedFailures = new goog.testing.ExpectedFailures();

  function tearDown() {
    expectedFailures.handleTearDown();
  }

  function testCreateFromNodeContents() {
    assertNotNull('Text range object can be created for element node',
        goog.dom.TextRange.createFromNodeContents(logo));
    assertNotNull('Text range object can be created for text node',
        goog.dom.TextRange.createFromNodeContents(logo2.previousSibling));
  }

  function testMoveToNodes() {
    var range = goog.dom.TextRange.createFromNodeContents(table2);
    range.moveToNodes(table2div, 0, table2div, 1, false);
    assertEquals('Range should start in table2div',
                 table2div,
                 range.getStartNode());
    assertEquals('Range should end in table2div',
                 table2div,
                 range.getEndNode());
    assertEquals('Range start offset should be 0',
                 0,
                 range.getStartOffset());
    assertEquals('Range end offset should be 0',
                 1,
                 range.getEndOffset());
    assertFalse('Range should not be reversed',
                range.isReversed());
    range.moveToNodes(table2div, 0, table2div, 1, true);
    assertTrue('Range should be reversed',
               range.isReversed());
    assertEquals('Range text should be "foo"',
                 'foo',
                 range.getText());
  }

  function testContainsTextRange() {
    var range = goog.dom.TextRange.createFromNodeContents(table2);
    var range2 = goog.dom.TextRange.createFromNodeContents(table2div);
    assertTrue('TextRange contains other TextRange',
            range.containsRange(range2));
    assertFalse('TextRange does not contain other TextRange',
            range2.containsRange(range));

    range = goog.dom.Range.createFromNodes(
            table2div.firstChild, 1, table2div.lastChild, 1);
    range2 = goog.dom.TextRange.createFromNodes(
            table2div.firstChild, 0, table2div.lastChild, 0);
    assertTrue('TextRange partially contains other TextRange',
            range2.containsRange(range, true));
    assertFalse('TextRange does not fully contain other TextRange',
            range2.containsRange(range, false));
  }

  function testContainsControlRange() {
    if (goog.userAgent.IE) {
      var range = goog.dom.ControlRange.createFromElements(table2);
      var range2 = goog.dom.TextRange.createFromNodeContents(table2div);
      assertFalse('TextRange does not contain ControlRange',
              range2.containsRange(range));
      range = goog.dom.ControlRange.createFromElements(logo2);
      assertTrue('TextRange contains ControlRange',
              range2.containsRange(range));
      range = goog.dom.TextRange.createFromNodeContents(table2);
      range2 = goog.dom.ControlRange.createFromElements(logo, logo2);
      assertTrue('TextRange partially contains ControlRange',
              range2.containsRange(range, true));
      assertFalse('TextRange does not fully contain ControlRange',
              range2.containsRange(range, false));
    }
  }

  function testGetStartPosition() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));

    // The start node is in the top left.
    var range = goog.dom.TextRange.createFromNodeContents(test3);
    var topLeft = goog.style.getPageOffset(test3.firstChild);

    if (goog.userAgent.IE) {
      // On IE the selection is as tall as its tallest element.
      var logoPosition = goog.style.getPageOffset(logo3);
      topLeft.y = logoPosition.y;

      if (!goog.userAgent.isVersionOrHigher('8')) {
        topLeft.x += 2;
        topLeft.y += 2;
      }
    }

    try {
      var result = assertNotThrows(goog.bind(range.getStartPosition, range));
      assertObjectEquals(topLeft, result);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }

  function testGetStartPositionNotInDocument() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));
    expectedFailures.expectFailureFor(goog.userAgent.IE &&
        !goog.userAgent.isVersionOrHigher('8'));

    var range = goog.dom.TextRange.createFromNodeContents(test3);

    goog.dom.removeNode(test3);
    try {
      var result = assertNotThrows(goog.bind(range.getStartPosition, range));
      assertNull(result);
    } catch (e) {
      expectedFailures.handleException(e);
    } finally {
      goog.dom.appendChild(document.body, test3);
    }
  }

  function testGetStartPositionReversed() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));

    // Simulate the user selecting backwards from right-to-left.
    // The start node is now in the bottom right.
    var firstNode = test3.firstChild.firstChild;
    var lastNode = test3.lastChild.lastChild;
    var range = goog.dom.TextRange.createFromNodes(
        lastNode, lastNode.nodeValue.length, firstNode, 0);
    var pageOffset = goog.style.getPageOffset(test3.lastChild);
    var bottomRight = new goog.math.Coordinate(
        pageOffset.x + test3.lastChild.offsetWidth,
        pageOffset.y + test3.lastChild.offsetHeight);

    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
      bottomRight.x += 2;
      bottomRight.y += 2;
    }

    try {
      var result = assertNotThrows(goog.bind(range.getStartPosition, range));
      assertObjectRoughlyEquals(bottomRight, result, 1);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }

  function testGetStartPositionRightToLeft() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));

    // Even in RTL content the start node is still in the top left.
    var range = goog.dom.TextRange.createFromNodeContents(test3Rtl);
    var topLeft = goog.style.getPageOffset(test3Rtl.firstChild);

    if (goog.userAgent.IE) {
      // On IE the selection is as tall as its tallest element.
      var logoPosition = goog.style.getPageOffset(logo3Rtl);
      topLeft.y = logoPosition.y;

      if (!goog.userAgent.isVersionOrHigher('8')) {
        topLeft.x += 2;
        topLeft.y += 2;
      }
    }

    try {
      var result = assertNotThrows(goog.bind(range.getStartPosition, range));
      assertObjectEquals(topLeft, result);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }

  function testGetEndPosition() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));

    // The end node is in the bottom right.
    var range = goog.dom.TextRange.createFromNodeContents(test3);
    var pageOffset = goog.style.getPageOffset(test3.lastChild);
    var bottomRight = new goog.math.Coordinate(
        pageOffset.x + test3.lastChild.offsetWidth,
        pageOffset.y + test3.lastChild.offsetHeight);

    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
      bottomRight.x += 6;
      bottomRight.y += 2;
    }

    try {
      var result = assertNotThrows(goog.bind(range.getEndPosition, range));
      assertObjectRoughlyEquals(bottomRight, result, 1);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }

  function testGetEndPositionNotInDocument() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));
    expectedFailures.expectFailureFor(goog.userAgent.IE &&
        !goog.userAgent.isVersionOrHigher('8'));

    var range = goog.dom.TextRange.createFromNodeContents(test3);

    goog.dom.removeNode(test3);
    try {
      var result = assertNotThrows(goog.bind(range.getEndPosition, range));
      assertNull(result);
    } catch (e) {
      expectedFailures.handleException(e);
    } finally {
      goog.dom.appendChild(document.body, test3);
    }
  }

  function testGetEndPositionReversed() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));

    // Simulate the user selecting backwards from right-to-left.
    // The end node is now in the top left.
    var firstNode = test3.firstChild.firstChild;
    var lastNode = test3.lastChild.lastChild;
    var range = goog.dom.TextRange.createFromNodes(
        lastNode, lastNode.nodeValue.length, firstNode, 0);
    var topLeft = goog.style.getPageOffset(test3.firstChild);

    if (goog.userAgent.IE) {
      // On IE the selection is as tall as its tallest element.
      var logoPosition = goog.style.getPageOffset(logo3);
      topLeft.y = logoPosition.y;

      if (!goog.userAgent.isVersionOrHigher('8')) {
        topLeft.x += 2;
        topLeft.y += 2;
      }
    }

    try {
      var result = assertNotThrows(goog.bind(range.getEndPosition, range));
      assertObjectEquals(topLeft, result);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }

  function testGetEndPositionRightToLeft() {
    expectedFailures.expectFailureFor(goog.userAgent.GECKO &&
        !goog.userAgent.isVersionOrHigher('2'));
    expectedFailures.expectFailureFor(goog.userAgent.IE &&
        !goog.userAgent.isVersionOrHigher('8'));

    // Even in RTL content the end node is still in the bottom right.
    var range = goog.dom.TextRange.createFromNodeContents(test3Rtl);
    var pageOffset = goog.style.getPageOffset(test3Rtl.lastChild);
    var bottomRight = new goog.math.Coordinate(
        pageOffset.x + test3Rtl.lastChild.offsetWidth,
        pageOffset.y + test3Rtl.lastChild.offsetHeight);

    if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
      bottomRight.x += 2;
      bottomRight.y += 2;
    }

    try {
      var result = assertNotThrows(goog.bind(range.getEndPosition, range));
      assertObjectRoughlyEquals(bottomRight, result, 1);
    } catch (e) {
      expectedFailures.handleException(e);
    }
  }
</script>
</body>
</html>
